﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.StackExchangeRedis;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;

using Leo.Core.HttpApi.Host.Filters;
using Leo.Core.HttpApi.Host.HealthCheck;
using Leo.Core.HttpApi.Host.Options;
using Leo.Core.HttpApi.Host.SwaggerFilter;
using Leo.Core.Jwt;

using StackExchange.Redis;

using Swashbuckle.AspNetCore.Filters;

using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.AntiForgery;
using Volo.Abp.AspNetCore.Mvc.ExceptionHandling;
using Volo.Abp.AspNetCore.Mvc.Json;
using Volo.Abp.AspNetCore.Mvc.Validation;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.Caching;
using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.EventBus.RabbitMq;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.RabbitMQ;
using Volo.Abp.Uow;
using Winton.Extensions.Configuration.Consul;

namespace Leo.Core.HttpApi.Host.Host
{
    public static class HttpApiHostModuleExtention
    {
        /// <summary>
        /// 配置数据保护
        /// </summary>
        /// <param name="context"></param>
        /// <param name="configuration"></param>
        /// <param name="hostingEnvironment"></param>
        public static void ConfigureDataProtection(
            this ServiceConfigurationContext context,
            string applicationName,
            string redisConfiguartionName = "Redis:Configuration"
        )
        {
            var configuration = context.Services.GetConfiguration();
            var hostingEnvironment = context.Services.GetHostingEnvironment();

            var dataProtectionBuilder = context.Services.AddDataProtection().SetApplicationName(applicationName);
            if (!hostingEnvironment.IsDevelopment())
            {
                var redis = ConnectionMultiplexer.Connect(configuration[redisConfiguartionName]);
                dataProtectionBuilder.PersistKeysToStackExchangeRedis(redis, $"{applicationName}-Protection-Keys");
            }
        }



        /// <summary>
        /// 配置后台任务
        /// </summary>
        /// <param name="context">配置处理委托</param>
        public static void ConfigureBackgroundJob(this ServiceConfigurationContext context, Action<AbpBackgroundJobWorkerOptions> optionsAction = null)
        {
            context.Services.Configure<AbpBackgroundJobOptions>(options =>
            {
                options.IsJobExecutionEnabled = true;
            });
            context.Services.Configure(optionsAction);
        }

        /// <summary>
        /// 配置EventBus
        /// </summary>
        public static void ConfigureEventBus(this ServiceConfigurationContext context)
        {
            context.Services.Configure<AbpRabbitMqOptions>(options =>
            {
            });
            context.Services.Configure<AbpRabbitMqEventBusOptions>(options =>
            {
            });

            context.Services.Configure<AbpDistributedEntityEventOptions>(options =>
            {
                //Enable for all entities
                options.AutoEventSelectors.AddAll();
            });
        }



        /// <summary>
        /// 配置缓存
        /// </summary>
        /// <param name="configuration"></param>
        public static void ConfigureCache(this ServiceConfigurationContext context, string keyPrefix)
        {
            context.Services.Configure<RedisCacheOptions>(options =>
            {
            });

            context.Services.Configure<AbpDistributedCacheOptions>(options => { options.KeyPrefix = keyPrefix; });
        }
        /// <summary>
        /// 配置Jwt认证
        /// </summary>
        /// <param name="context"></param>
        /// <param name="configuration"></param>
        public static void ConfigureJwtAuthentication(this ServiceConfigurationContext context)
        {
            var configuration = context.Services.GetConfiguration();
            var jwtOption = new JwtOption();
            configuration.Bind(JwtOption.SectionName, jwtOption);
            context.Services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(options =>
            {
                options.RequireHttpsMetadata = false; // 禁用 Https 选项
                options.Authority = jwtOption.Authority;
                options.Audience = jwtOption.Audience;
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    
                    SaveSigninToken = true,//保存token,后台验证token是否生效(重要)
                    ValidateIssuer = true,//是否验证Issuer
                    ValidateAudience = true,//是否验证Audience
                    ValidateLifetime = true,//是否验证失效时间
                    ValidateIssuerSigningKey = true,//是否验证SecurityKey
                    ValidAudience = jwtOption.Audience,//Audience
                    ValidIssuer = jwtOption.Issuer,//Issuer，这两项和前面签发jwt的设置一致
                    IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(jwtOption.SecurityKey))
                };
                options.Events = new JwtBearerEvents
                {
                    OnAuthenticationFailed = context =>
                    {
                        if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                        {
                            context.Response.Headers.Add("act", "expired");
                        }
                        return Task.CompletedTask;
                    }
                };
            });
        }



        /// <summary>
        /// 配置健康检查
        /// </summary>
        /// <param name="context"></param>
        /// <param name="configuration"></param>
        public static void ConfigureHealthCheck(this ServiceConfigurationContext context, string healthCheckName)
        {
            context.Services.AddHealthChecks().AddCheck<HttpApiHealthCheck>(healthCheckName);
        }


        /// <summary>
        /// 配置Abp工作单元
        /// </summary>
        /// <param name="context"></param>
        public static void ConfigureAbpUnitOfWorkOptions(this ServiceConfigurationContext context)
        {
            context.Services.Configure<AbpUnitOfWorkDefaultOptions>(options =>
            {
                options.TransactionBehavior = UnitOfWorkTransactionBehavior.Auto;
            });
        }

        /// <summary>
        /// 配置自动AppService生成Api 
        /// </summary>
        public static void ConfigureConventionalControllers<TModule>(this ServiceConfigurationContext context) where TModule : AbpModule
        {
            context.Services.Configure<AbpAspNetCoreMvcOptions>(options =>
            {
                options.ConventionalControllers.Create(typeof(TModule).Assembly);
            });
        }

        /// <summary>
        /// 配置CSRF
        /// </summary>
        /// <param name="context"></param>
        public static void ConfigureAbpAntiForgeryOptions(this ServiceConfigurationContext context, List<string> ignoredHttpMethods, bool autoValidate = false)
        {
            context.Services.Configure<AbpAntiForgeryOptions>(options =>
            {
                options.AutoValidate = autoValidate;
                foreach (var validateIgnoredHttpMethod in ignoredHttpMethods)
                {
                    options.AutoValidateIgnoredHttpMethods.Add(validateIgnoredHttpMethod);
                }
            });
        }

        /// <summary>
        /// 配置ABP 异常拦截器
        /// </summary>
        /// <param name="context"></param>
        public static void ConfigureAbpException<TExceptionFilter>(this ServiceConfigurationContext context)
            where TExceptionFilter
            : AbpBaseExceptionFilter
        {
            context.Services.AddSingleton<IExceptionToErrorInfoConverter, AbpBaseExceptionToErrorInfoConverter>();
            context.Services.AddMvc(options =>
            {
                options.Filters.ReplaceOne(x => (x as ServiceFilterAttribute)?.ServiceType?.Name == nameof(AbpExceptionFilter), new ServiceFilterAttribute(typeof(TExceptionFilter)));
            });
        }

        /// <summary>
        /// 拦截验证信息异常，格式化验证错误返回信息
        /// </summary>
        /// <typeparam name="TModelValidActionFilter"></typeparam>
        /// <param name="context"></param>
        public static void ConfigModelValidateErrorResultFormat<TModelValidActionFilter>(this ServiceConfigurationContext context) where TModelValidActionFilter
            : AbpBaseModelValidActionFilterAttribute
        {
            context.Services.AddControllers(options =>
            {
                var filterMetadata = options.Filters.FirstOrDefault(
                    x => x is ServiceFilterAttribute attribute &&
                    attribute.ServiceType.Equals(typeof(AbpValidationActionFilter)));
                // 移除 AbpValidationActionFilter
                options.Filters.Remove(filterMetadata);
                // 必须设置为最小值 abp还有-1的优先级
                options.Filters.Add<TModelValidActionFilter>(int.MinValue);
            });
        }

        /// <summary>
        /// 配置MVC默认JSON序列化方案为Json.Net
        /// </summary>
        /// <param name="context"></param>
        //[Obsolete(".net7.0版本移除了AbpMvcNewtonsoftJsonOptionsSetup类")]
        //public static void ConfigureMvcNewtonsoftJson(this ServiceConfigurationContext context)
        //{
        //    context.Services.TryAddEnumerable(ServiceDescriptor.Transient<IConfigureOptions<MvcNewtonsoftJsonOptions>, AbpMvcNewtonsoftJsonOptionsSetup>());
        //    context.Services.AddMvc().AddNewtonsoftJson(options =>
        //    {
        //        options.SerializerSettings.Converters.
        //               Add(new Newtonsoft.Json.Converters.StringEnumConverter());
        //    });
        //}

        public static void ConfigureSwaggerServices(this ServiceConfigurationContext context, string title, string version, bool enableJwt = true)
        {
            context.Services.AddAbpSwaggerGen(
                options =>
                {
                    options.SwaggerDoc("v1", new OpenApiInfo { Title = title, Version = version });
                    options.DocInclusionPredicate((docName, description) => true);
                    options.CustomSchemaIds(type => type.FullName);
                    options.IgnoreObsoleteActions();
                    options.IgnoreObsoleteProperties();
                    options.EnableAnnotations();
                    options.DocumentFilter<HiddenSwaggerApiFilter>();

                    #region swagger 用 Jwt验证
                    if (enableJwt)
                    {
                        //开启权限小锁
                        options.OperationFilter<AddResponseHeadersFilter>();
                        options.OperationFilter<AppendAuthorizeToSummaryOperationFilter>();
                        //在header中添加token，传递到后台
                        options.OperationFilter<SecurityRequirementsOperationFilter>();
                        options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
                        {
                            Description = "JWT授权(数据将在请求头中进行传递)直接在下面框中输入Bearer {token}(注意两者之间是一个空格) \"",
                            Name = "Authorization",//jwt默认的参数名称
                            In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
                            Type = SecuritySchemeType.ApiKey
                        });
                    }
                    #endregion

                });

            context.Services.AddSwaggerGenNewtonsoftSupport();
        }


        /// <summary>
        /// 读取全局跨域配置
        /// </summary>
        /// <param name="context"></param>
        /// <param name="configNameIsGloablCorsOrigins"></param>
        /// <returns></returns>
        public static bool GetIsGloablCorsOrigins(this ServiceConfigurationContext context, string configNameIsGloablCorsOrigins = "App:IsGloablCorsOrigins")
        {
            var configuration = context.Services.GetConfiguration();
            var isGloablCorsOrigins = bool.Parse(configuration[configNameIsGloablCorsOrigins]);
            return isGloablCorsOrigins;
        }

        /// <summary>
        /// 配置国际化
        /// </summary>
        public static void ConfigureLocalization(this ServiceConfigurationContext context, List<LanguageInfo> languageInfos)
        {
            context.Services.Configure<AbpLocalizationOptions>(options =>
            {
                options.Languages.AddRange(languageInfos);
            });
        }


        public static void UseRequestLocalization(this IApplicationBuilder app, List<LanguageInfo> languageInfos, LanguageInfo defaultLanguage)
        {
            var supportedCultures = languageInfos.Select(p => p.CultureName).ToArray();
            var supportedUICultures = languageInfos.Select(p => p.UiCultureName).ToArray();

            app.UseAbpRequestLocalization(options =>
            {
                options.SetDefaultCulture(defaultLanguage.CultureName)
                    .AddSupportedCultures(supportedCultures)
                    .AddSupportedUICultures(supportedUICultures);
            });
        }





        /// <summary>
        /// 配置跨域
        /// </summary>
        /// <param name="context"></param>
        /// <param name="configuration"></param>
        public static void ConfigureCors(this ServiceConfigurationContext context, string corsPolicyName, bool isGlobalCors)
        {
            var configuration = context.Services.GetConfiguration();

            if (isGlobalCors)
            {
                context.Services.AddCors(options =>
                {
                    options.AddPolicy(corsPolicyName, builder =>
                    {
                        builder
                            .AllowAnyOrigin()
                            .AllowAnyHeader()
                            .AllowAnyMethod();
                    });
                });
            }
            else
            {
                context.Services.AddCors(options =>
                {
                    options.AddPolicy(corsPolicyName, builder =>
                    {
                        builder
                            .WithOrigins(
                                configuration["App:CorsOrigins"]
                                    .Split(",", StringSplitOptions.RemoveEmptyEntries)
                                    .Select(o => o.RemovePostFix("/"))
                                    .ToArray()
                            )
                            .WithAbpExposedHeaders()
                            .SetIsOriginAllowedToAllowWildcardSubdomains()
                            .AllowAnyHeader()
                            .AllowAnyMethod()
                            .AllowCredentials();
                    });
                });
            }
        }

        /// <summary>
        /// consul 配置
        /// </summary>
        /// <param name="configBuilder">构建配置器</param>
        /// <param name="configKey">Consul Key </param>
        /// <param name="consulUrl">Consul 地址</param>
        /// <param name="optionsAction">Consul 配置源(可不填)</param>
        /// <returns></returns>
        public static IConfigurationBuilder AddConsulConfigCenter(this IConfigurationBuilder configBuilder, 
            string configKey, string consulUrl, Action<IConsulConfigurationSource> optionsAction = null)
        {
            // 加载Consul配置
            configBuilder.AddConsul(configKey, options =>
            {
                options.ConsulConfigurationOptions = cco => { cco.Address = new Uri(consulUrl); }; // 1、consul地址
                options.Optional = true; // 2、配置选项
                options.ReloadOnChange = true; // 3、配置文件更新后重新加载
                options.OnLoadException = exceptionContext =>
                {
                    exceptionContext.Ignore = true;
                }; // 4、忽略异常

                if (optionsAction != null)
                    optionsAction.Invoke(options);
            });

            return configBuilder;
        }

        /// <summary>
        /// host 添加consul配置
        /// </summary>
        public static IHostBuilder ConfigureAppConfigurationConsulSupport(this IHostBuilder hostBuilder) 
        {
            try
            {
                hostBuilder.ConfigureAppConfiguration((hostingContext, config) =>
                {
                    // 加载默认配置信息到Configuration（环境变量，命令行，配置文件）
                    hostingContext.Configuration = config.Build();
                    var startUpConfiguration = hostingContext.Configuration;

                    // 加载服务发现配置
                    var serviceDiscoveryOption = startUpConfiguration.GetOptionByName<ServiceDiscoveryOption>(ServiceDiscoveryOption.SectionName);

                    // 获取当前应用部署名
                    var appDeployName = startUpConfiguration["App:DeployName"];

                    var consulKey = $"{serviceDiscoveryOption.ServiceName}.{appDeployName}.appsettings.json";

                    // 加载Consul配置
                    config.AddConsulConfigCenter(consulKey, serviceDiscoveryOption.ConsulUrl);

                    //重新加入环境变量配置提高环境变量的优先级
                    config.AddEnvironmentVariables();

                    // 5、consul中加载的配置信息加载到Configuration对象，然后通过Configuration 对象加载项目中
                    hostingContext.Configuration = config.Build();
                });
            }
            catch 
            {
                
            }
            return hostBuilder;
        }
    }
}
